package com.summatech.gwt.mvp.server.util;

import java.io.PrintWriter;

import com.google.gwt.core.ext.Generator;
import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.UnableToCompleteException;
import com.google.gwt.core.ext.typeinfo.JClassType;
import com.google.gwt.core.ext.typeinfo.JMethod;
import com.google.gwt.core.ext.typeinfo.NotFoundException;
import com.google.gwt.core.ext.typeinfo.TypeOracle;
import com.google.gwt.user.rebind.ClassSourceFileComposerFactory;
import com.google.gwt.user.rebind.SourceWriter;
import com.summatech.gwt.mvp.client.GroupEventBus;

/**
 * Generates the GroupEventList implementation class that contains a list of the event types in
 * GroupEventBus interface
 * 
 * @author Javier Ochoa
 */
public class GroupEventListGenerator extends Generator {

    /** Simple name of class to be generated */
    private String className;

    /** Package name of class to be generated */
    private String packageName;

    /** Simple reference to the context typeOracle instance */
    private TypeOracle typeOracle;

    // inherited generator method
    @Override
    public String generate(TreeLogger logger, GeneratorContext context, String typeName) throws UnableToCompleteException {
        this.typeOracle = context.getTypeOracle();

        try {
            // get classType and save instance variables
            JClassType classType = typeOracle.getType(typeName);
            packageName = classType.getPackage().getName();
            className = classType.getSimpleSourceName() + "Impl";
            // Generate class source code
            generateClass(logger, context);

        } catch (NotFoundException e) {
            // record to logger that Map generation threw an exception
            logger.log(TreeLogger.ERROR, "GroupEventList generation ERROR!!!", e);
        }

        // return the fully qualified name of the class generated
        return packageName + "." + className;

    }

    /**
     * Generate source code for new class. Class extends <code>ArrayList</code>.
     * 
     * @param logger
     *            Logger object
     * @param context
     *            Generator context
     */
    private void generateClass(TreeLogger logger, GeneratorContext context) {

        // get print writer that receives the source code
        PrintWriter printWriter = context.tryCreate(logger, packageName, className);

        // print writer if null, this class has ALREADY been generated, return
        if (printWriter == null)
            return;

        // init composer, set class properties, create source writer
        ClassSourceFileComposerFactory composer = null;
        composer = new ClassSourceFileComposerFactory(packageName, className);
        composer.setSuperclass("java.util.ArrayList");

        SourceWriter sourceWriter = composer.createSourceWriter(context, printWriter);

        generateConstructor(sourceWriter);

        // close generated class
        sourceWriter.outdent();
        sourceWriter.println("}");

        // commit generated class
        context.commit(logger, printWriter);
    }

    /**
     * Generate source code for the default constructor. Create default constructor, call super(),
     * and insert all methods declared in GroupEventBus interface.
     * 
     * @param sourceWriter
     *            Source writer to output source code
     */
    private void generateConstructor(SourceWriter sourceWriter) {
        // start constructor source generation
        sourceWriter.println("public " + className + "() { ");
        sourceWriter.indent();
        sourceWriter.println("super();");
        try {

            JClassType jClassType = typeOracle.getType(GroupEventBus.class.getCanonicalName());
            JMethod[] groupEventBusMethods = jClassType.getMethods();
            for (JMethod method : groupEventBusMethods) {
                sourceWriter.println("add(\"" + Generator.escape(method.getName()) + "\");");
            }
        } catch (NotFoundException e) {
            e.printStackTrace();
        }

        // end constructor source generation
        sourceWriter.outdent();
        sourceWriter.println("}");

    }

}